﻿namespace System
{
	using System.Threading;
	using System.Threading.Tasks;
	using System.Runtime.CompilerServices;

	/// <summary>
	/// <c>AsyncLazy</c> is a fully asynchronous variant of the <see cref="Lazy"/>
	/// type.
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <remarks>
	/// This class allows the the lazy value to be awaited without blocking the
	/// caller resulting in greater efficiencies for code logic and system resources.
	/// This class also implements a custom awaiter so instead of writing;
	///		T result = await m_myLazyObject.Value;
	///	you can instead write;
	///		T result = await m_myLazyObject;
	/// </remarks>
	public class AsyncLazy<T> : Lazy<Task<T>>
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="AsyncLazy&lt;T&gt;"/> class.
		/// </summary>
		/// <param name="valueFactory">The value factory.</param>
		public AsyncLazy(Func<T> valueFactory)
			: base(() => Task.Factory.StartNew(valueFactory))
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="AsyncLazy&lt;T&gt;"/> class.
		/// </summary>
		/// <param name="taskFactory">The task factory.</param>
		/// <remarks>
		/// The task-factory will be invoked on a seperate thread when the lazy
		/// value is first requested and the initialisation function itself is
		/// free to take as long as it needs before returning the lazy value
		/// proper.
		/// </remarks>
		public AsyncLazy(Func<Task<T>> taskFactory)
			:this(taskFactory, false)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="AsyncLazy&lt;T&gt;"/> class.
		/// </summary>
		/// <param name="taskFactory">The task factory.</param>
		/// <param name="synchronousInit">
		/// if set to <c>true</c> then the supplied task-factory method will be
		/// invoked synchronously when the lazy value is first requested,
		/// otherwise, the task-factory method will itself be invoked on an
		/// background thread.
		/// </param>
		/// <remarks>
		/// The fully asynchronous variant of this class is very efficient in
		/// that awaiting on the lazy value property will be very fast with
		/// very little blocking.
		/// </remarks>
		public AsyncLazy(Func<Task<T>> taskFactory, bool synchronousInit)
			: base(
				() =>
				{
					if (synchronousInit)
					{
						return taskFactory();
					}
					else
					{
						return Task.Factory.StartNew(() => taskFactory()).Unwrap();
					}
				})
		{
		}

		/// <summary>
		/// Gets the awaiter.
		/// </summary>
		/// <returns></returns>
		public TaskAwaiter<T> GetAwaiter()
		{
			return Value.GetAwaiter();
		}
	}
}
